#include <fstream>
#include <future>
#include <iostream>
#include <list>
#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>
#include <thread>

#include "config.hpp"
#include "global.hpp"
#include "logger.hpp"

int main()
{
    // InitLogger("~/.log/daemon.log");
    InitLogger("log/daemon.log");
    spdlog::info("Start daemon");
    Global &g = Global::Instance();
    g.asio_context = AD::AsioContext::CreatAsioContext();

    std::ifstream ifs("rules.rule.json");
    if (ifs.good())
    {
        ifs.close();
        g.config = std::make_shared<Config>("rules.rule.json");
        spdlog::info("Config loaded: {}", "rules.rule.json");
    }
    else
    {
        std::ifstream ifs("~/.daemon/rules.rule.json");
        if (ifs.good())
        {
            ifs.close();
            g.config = std::make_shared<Config>("~/.daemon/rules.rule.json");
            spdlog::info("Config loaded: {}", "~/.daemon/rules.rule.json");
        }
        else
        {
            spdlog::error("Config not found, quit daemon.");
            return -1;
        }
    }

    // print config
    spdlog::info("Config loaded: rules:count = {}", g.config->rules.size());
    for (const Rule &rule : g.config->rules)
    {
        if (rule.event == nullptr)
            spdlog::info(" Rule:<null>");
        else
            spdlog::info(" Rule:{}<{}>", rule.event->Name(), rule.event->Name());
        for (auto action : rule.actions)
        {
            if (action == nullptr)
                spdlog::info("\t Action:<null>");
            else
                spdlog::info("\t Action:{}<{}>", action->Name(), action->Type());
        }
    }
    // check rule and action
    for (const Rule &rule : g.config->rules)
    {
        if (rule.event == nullptr)
        {
            spdlog::error("detected null event in rule, quit daemon.");
            QuitLogger();
            return -1;
        }
        for (auto action : rule.actions)
        {
            if (action == nullptr)
            {
                spdlog::error("detected null action in rule, quit daemon.");
                QuitLogger();
                return -1;
            }
        }
    }

    spdlog::info("Enter main loop, start daemon.");
    while (true)
    {
        g.asio_context->SpinOnce();
        // check event and execute actions
        for (Rule &rule : g.config->rules)
        {
            if (rule.IsRunning())
            {
                continue;
            }
            if (rule.event->IsAct())
            {
                rule.AsyncExecute();
            }
        }
        // remove finished tasks
        for (Rule &rule : g.config->rules)
        {
            if (rule.IsRunning() && rule.CheckFinished())
            {
                spdlog::info("Rule {}<{}> finished.", rule.event->Name(), rule.event->Type());
            }
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    spdlog::info("End daemon");
    QuitLogger();
    return 0;
}
